查看原文
其他

ARM64逆向基础

哆啦安全 2022-11-13

The following article is from 游戏安全攻防 Author 游戏安全攻防

为什么要学ARM64?

android 5.0系统就开始引入Arm64-v8a,它用于支持全新的AArch64架构,这个架构也就是我们要学习的arm64汇编。目前android系统已经发展到anroid 11版本。因此现在主流的apk都是支持AArch64架构。那么我们利用IDA(反汇编工具)进行静态逆向分析so文件、或者IDA动态调试so文件,都需要和arm64汇编代码打交道,因此对于学习掌握好ARM64汇编对阅读反汇编代码能达到事半功倍的效果。

ARM64汇编由什么组成的?

• 汇编中共有34个寄存器。其中包括31个通用寄存器、SP寄存器、PC寄存器,CPSR寄存器。•31个通用寄存器中:
X0-X30:表示是64位的寄存器。
W0-W30:表示是32位的寄存器。
•X31 : 也称为零寄存器(它一般用于变量的初始化),它也有两表现形式:XZR:表示是64位的零寄存器, 它在内存中是用8个字节存储。WZR:表示是32位的零寄存器,它在内存中是用4个字节存储。•SP : 保存栈指针(栈顶指针),使用SP或WSP来进行对SP寄存器的访问,也就是用于操作局部变量地址。•PC:程序计数器(PC指针寄存器),它用于指向即将要执行的下一条指令。•CPSR:状态寄存器•FP(X29):保存栈帧地址(栈底指针)•LP(X30):通常称X30为程序的链接寄存器,保存子程序结束后需要执行的下一条指令。

通用寄存器作用是什么?

•在汇编代码中使用 X0 - X30寄存器进行访问操作数据时,它就表示的是一个64位的数据。•在汇编代码中使用 W0 - W30寄存器进行访问操作数据时,它表示的是一个32位的数据。

栈寄存器是什么怎么用?

1.栈结构及作用?

栈是一种具有特殊的访问方式的存储空间,先进后出(后进先出),它是从高地址到低地址的, 栈底是高地址,栈顶是低地址。
它的主要的作用:用于存放参数和局部变量(临时变量)。

 2. 栈寄存器表示指令有那些?

SP:栈顶寄存器
FP:栈底寄存器

 3. 操作栈寄存器的有那些指令?

STP指令:表示入栈指令
LDP指令:表示出栈指令
入栈操作的汇编代码片段

出栈操作的汇编代码片段

状态寄存器由那些状态组成?

ARM64的汇编指令集中,有一部分指令的执行时影响状态寄存器的,比如add、sub、or汇编指令等,他们大都是运算指令(进行逻辑或算数运算)

CPSR的低8位(包括I、F、T和M[0~4])称为控制位,程序无法修改,除非CPU运行于特权模式下,程序才能修改控制位! N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行。

 1. N(Negative)标志

CPSR的第31位是 N,符号标志位。它记录相关指令执行后,其结果是否为负。如果为负 N = 1,如果是非负数 N = 0。

 2. Z(Zero)标志

CPSR的第30位是Z,零标志位。它记录相关指令执行后,其结果是否为0。如果结果为0,那么Z = 1;如果结果不为0,那么Z = 0。

 3. C(Carry)标志

CPSR的第29位是C,进位标志位。一般情况下,进行无符号数的运算。
加法运算(add指令):当运算结果产生了进位时(无符号数溢出),C=1,否则C=0。减法运算(sub指令):当运算时产生了借位时(无符号数溢出),C=0,否则C=1。

4. V(Overflow)溢出标志

CPSR的第28位是V,溢出标志位。在进行有符号数运算的时候,如果超过了机器所能标识的范围,称为溢出。
下面几种情况会出现溢出
正数 + 正数 为负数 溢出
负数 + 负数 为正数 溢出
正数 + 负数 不可能溢出

ARM64特有的汇编指令

•adrp指令(address page)

它是一条地址读取指令,是用于计算指定的数据地址到当前PC值的相对偏移。
下面是它具体的用法及解释

得到一个大小为4KB的页的基址,而且在该页中有全局变量g的地址;ADRP就是讲该页的基址存到寄存器X6中;
ADD指令会算出g的地址,X6+#_g@PAGEOFF,#_g@PAGEOFF是一个偏移量;这样就得到了g的地址X6;

•内存读写指令(ldr、ldur、ldp, str、stur、stp)

STR、STP、STUR为存储数据指令(注:以ST开头的表示为存储指令)
LDR、LDP、LDUR为取出数据指令(注:以LD开头的表示为取出数据)
以下是具体指令说明及汇编代码分析
STR指令:将数据从寄存器中读出来,存储到内存中。
STUR指令:将寄存器中的负数数据读取出来,存放到内存中。
STP指令:表示入栈指令。


LDR指令:将数据从内存中取出来,存放到寄存器中。
LDUR指令:将内存中负数的数据取出来,并存放到寄存器中。
LDP指令:表示出栈指令

ARM64汇编的函数有那些需要重点关注?



1.函数调用约定是什么约定?
ARM64使用的是ATPCS(ARM-Thumb Procedure Call Standard/ARM-Thumb过程调用标准)的函数调用约定。
2.函数需要用到到的指令有那些?
B:无条件跳转,一般是函数内部的if、switch条件判断的跳转
Bl:带函数返回值的跳转,一般是用于调用其他的函数。RET:子程序返回指令,返回地址默认保存在X30寄存器(LR链接寄存器)
LR:保存子程序结束后需要执行的下一条指令
PC:表示当前执行的指令的地址。
3.函数的参数怎么存储传递?
3.1. 通常情况下函数的参数存放在X0-X7(32位的为W0-W7)这8个寄存器,如果函数参数超过8个,那么就需要用到栈存储的方式来存储参数。
3.2. 函数参数如果少于8个参数的那么依次从左往右传参,如果多于8个的参数那么依次从右往左进行入栈的。
3.3 函数中8个参数和9个参数的具体情况(不过在开发过程中参数超过8个的情况是比较少的,因此在逆向过程中这种超过8个参数的情况也会比较少的)
      3.3.1函数中有八个参数的,直接用寄存器W0-W7表示       源代码:


       ARM64汇编代码:

      3.3.2 函数中有九个参数的,需要用栈寄存器来传递参数     源代码:

     ARM64汇编代码:

4.函数中的堆栈怎么平衡?
堆栈平衡需要被调用者进行恢复平衡。
       


5.函数的返回值怎么进行接收?
函数的返回值通常情况下是存放在X0寄存器里面,ret将X0寄存器的地地址值放入X30寄存器(也就是LR链接寄存器)。


安卓逆向之ARM汇编基础


大佬们留个关注再走呗,后续精彩文章不断

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存